package com.example.demo.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.demo.dao.ParameterDao;
import com.example.demo.domain.entity.ParameterAttributeValueEntity;
import com.example.demo.domain.entity.ParameterDefinitionEntity;
import com.example.demo.domain.entity.ParameterEntity;
import com.example.demo.domain.entity.ParameterValueEntity;
import com.example.demo.domain.model.Parameter;
import com.example.demo.domain.model.ParameterAttributeValue;
import com.example.demo.domain.model.ParameterDefinition;
import com.example.demo.domain.model.ParameterValue;
import com.example.demo.service.ParameterDefinitionService;
import com.example.demo.service.ParameterService;
import com.example.demo.service.ParameterValueService;
import com.example.demo.service.ParameterVerifyService;
import com.example.demo.util.IdUtil;
import com.example.demo.util.ParameterEntityToVO;
import com.example.demo.util.ParameterVOToEntity;
import com.example.demo.util.json.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ParameterServiceImpl implements ParameterService {
    @Autowired
    ParameterDao parameterDao;
    @Autowired
    ParameterValueService parameterValueService;
    @Autowired
    ParameterAttributeValueServiceImpl parameterAttributeValueService;
    @Autowired
    ParameterVOToEntity parameterVOToEntity;
    @Autowired
    ParameterEntityToVO parameterEntityToVO;
    @Autowired
    ParameterVerifyService parameterVerifyService;

    @Autowired
    ParameterDefinitionService parameterDefinitionService;

    @Override
    public boolean existParameterById(Long id) {
        if (id == null) {
            return false;
        }
        return parameterDao.selectById(id) != null;
    }


    @Override
    public boolean existParameterByCode(String code) {
        LambdaQueryWrapper<ParameterEntity> queryWrapper = Wrappers.lambdaQuery(ParameterEntity.class).eq(ParameterEntity::getCode, code);
        return parameterDao.selectOne(queryWrapper) != null;
    }

    @Override
    public void validateAndSaveParameter(Parameter parameter) {
        Assert.notNull(parameter, "Assert parameter isNull");
        parameter.setOperatorType("create");
        parameterVerifyService.validate(parameter);
        saveParameter(parameter, IdUtil.nextId());
    }

    @Override
    public void validateAndUpdateParameter(Parameter parameter) {
        Assert.notNull(parameter, "Assert parameter isNull");
        parameter.setOperatorType("update");
        parameterVerifyService.validate(parameter);
        mergeParameter(parameter);
    }

    @Override
    public void saveParameter(Parameter parameter, long rootId) {
        // 1.0 p
        Long pid = saveOneP(parameter, rootId);
        // 2.0 pd
        parameterDefinitionService.savePdList(parameter, rootId, pid);
        // 3.0 pv
        parameterValueService.savePvList(parameter, rootId, pid);
        String jsonString = JSON.toJSONString(parameter);
        FileUtils.writeJOSN(jsonString,parameter.getName());
    }


    private void mergeParameter(Parameter parameter) {
        Long id = parameter.getId();
        Long rootId = parameter.getParameterRootId();
        boolean exist = existParameterById(id);
        ParameterEntity p1 = parameterVOToEntity.transferParameterToEntity(parameter);

        if (exist) {
            ParameterEntity p0 = parameterDao.selectById(id);
            if (!p0.compare(p1)) {
                parameterDao.updateById(p1);
            }

            List<ParameterDefinition> pds = parameter.getParameterDefinitions();
            if (!pds.isEmpty()) {
                for (ParameterDefinition pd : pds) {
                    pd.setParameterRootId(rootId);
                    Long pd1Id = pd.getId();
                    boolean existPd = parameterDefinitionService.existById(pd1Id);
                    if (existPd) {
                        ParameterDefinitionEntity pd0 = parameterDefinitionService.getById(pd1Id);
                        ParameterDefinitionEntity pd1 = parameterVOToEntity.transferParameterDefinitionToEntity(pd);
                        if (!pd0.compare(pd1)) {
                            parameterDefinitionService.updateById(pd1);
                        }
                    } else {
                        parameterDefinitionService.saveOnePd(rootId, id, pd);
                    }
                }
            }

            List<ParameterValue> pvs = parameter.getParameterValues();
            if (!pvs.isEmpty()) {
                for (ParameterValue pv : pvs) {
                    pv.setParameterRootId(rootId);
                    Long pvId = pv.getId();
                    boolean existPv = parameterValueService.existById(pvId);
                    if (existPv) {
                        ParameterValueEntity pv0 = parameterValueService.getById(pvId);
                        ParameterValueEntity pv1 = parameterVOToEntity.transferParameterValueToEntity(pv);
                        if (!pv0.compare(pv1)) {
                            parameterValueService.updateById(pv1);
                        }
                        List<ParameterAttributeValue> pavs = pv.getParameterAttributeValues();
                        if (pavs!=null && !pavs.isEmpty()) {
                            for (ParameterAttributeValue pav : pavs) {
                                pav.setParentId(pvId);
                                pav.setParameterRootId(rootId);
                                String attributeCode = pav.getAttributeCode();
                                boolean existPav = parameterAttributeValueService.existByCombinationKey(attributeCode,pvId);
                                if (existPav) {
                                    ParameterAttributeValueEntity pav0 = parameterAttributeValueService.getByCombinationKey(attributeCode,pvId);
                                    ParameterAttributeValueEntity pav1 = parameterVOToEntity.transferParameterAttributeValueToEntity(pav);
                                    if (!pav0.compare(pav1)) {
                                        log.info("merge pav is exist updateById {}", attributeCode);
                                        pav1.setId(pav0.getId());
                                        parameterAttributeValueService.updateById(pav1);
                                    }
                                } else {
                                    log.info("merge pav is not exist saveOnePav {}", attributeCode);
                                    parameterAttributeValueService.saveOnePav(rootId, pvId, pav);
                                }
                            }
                        }
                    } else {
                        parameterValueService.saveOnePv(rootId, id, pv);
                    }
                }
            }
        } else {
            saveParameter(parameter, IdUtil.nextId());
        }
    }


    @Override
    public boolean deleteParameterByCode(String code) {
        LambdaQueryWrapper<ParameterEntity> q1 = Wrappers.lambdaQuery(ParameterEntity.class).eq(ParameterEntity::getCode, code);
        ParameterEntity parameterEntity = parameterDao.selectOne(q1);
        return deleteParameter(parameterEntity);
    }

    @Override
    public boolean deleteParameterById(Long id) {
        ParameterEntity parameterEntity = parameterDao.selectById(id);
        return deleteParameter(parameterEntity);
    }

    @Override
    public Parameter getParameterById(Long id) {
        ParameterEntity parameterEntity = parameterDao.selectById(id);
        return creatParameter(parameterEntity);
    }


    @Override
    public Parameter getParameterByCode(String code) {
        LambdaQueryWrapper<ParameterEntity> q1 = Wrappers.lambdaQuery(ParameterEntity.class).eq(ParameterEntity::getCode, code);
        ParameterEntity parameterEntity = parameterDao.selectOne(q1);
        return creatParameter(parameterEntity);
    }

    @Override
    public Parameter creatParameter(ParameterEntity parameterEntity) {
        Parameter parameter = parameterEntityToVO.transferParameterToVO(parameterEntity);
        Long parameterRootId = parameter.getParameterRootId();

        List<ParameterDefinitionEntity> parameterDefinitionEntities = parameterDefinitionService.listByRootId(parameterRootId);
        List<ParameterDefinition> pd = parameterDefinitionEntities.stream().map(pds -> parameterEntityToVO.transferParameterDefinitionToVO(pds)).collect(Collectors.toList());
        parameter.setParameterDefinitions(pd);

        List<ParameterValueEntity> parameterValueEntities = parameterValueService.listByRootId(parameterRootId);

        List<ParameterAttributeValueEntity> parameterAttributeValueEntities = parameterAttributeValueService.listByPid(parameterRootId);
        Map<Long, List<ParameterAttributeValue>> pavMap = parameterAttributeValueEntities.stream().map(pav -> parameterEntityToVO.transferParameterAttributeValueToVO(pav)).collect(Collectors.groupingBy(ParameterAttributeValue::getParentId));

        List<ParameterValue> pv = parameterValueEntities.stream().map(pvs -> {
            ParameterValue parameterValue = parameterEntityToVO.transferParameterValueToVO(pvs);
            parameterValue.setParameterAttributeValues(pavMap.get(pvs.getId()));
            return parameterValue;
        }).collect(Collectors.toList());

        parameter.setParameterValues(pv);
        return parameter;
    }

    private boolean deleteParameter(ParameterEntity parameterEntity) {
        Long pid = parameterEntity.getId();
        Long parameterRootId = parameterEntity.getParameterRootId();
        parameterDefinitionService.deleteByPid(pid);

        parameterValueService.deleteByPid(pid);

        parameterAttributeValueService.deleteByRootId(parameterRootId);

        parameterDao.deleteById(pid);
        return true;
    }


    public Long saveOneP(Parameter parameter, long rootId) {
        ParameterEntity p = parameterVOToEntity.transferParameterToEntity(parameter);
        p.setParameterRootId(rootId);
        p.setCreateDate(new Date());

        parameterDao.insert(p);

        Long pid = p.getId();
        parameter.setId(pid);
        return pid;
    }

}
